/**
 * 防抖
 * immediate: 是否立即执行
 */
function debounce(fn, wait, immediate = false) {
    let self = this,
        timer = null;
    return function() {
        let exec = immediate && !timer;
        clearTimeout(timer);
        timer = setTimeout(() => {
            clearTimeout(timer);
            timer = null;
            !immediate && fn.call(self);
        }, wait)
        exec ? fn.call(self) : null
    }
}

document.getElementById('btn').onclick = debounce(function() {
    console.log(111)
}, 500, true)


/**
 * 节流 
 */
function throttle(func, wait) {
  if (typeof func !== "function") throw new TypeError("func must be an function!");
  if (typeof wait === "undefined") wait = 500; 
  
  let timer = null, previous = 0;
  return function proxy(...args) {
    let now = new Date(),
        remaining = wait - (now - previous), 
        self = this;
    if(remaining <= 0) {
      clearTimeout(timer);
      timer = null;
      previous = now;
      func.call(self, ...args);
    }
    if(!timer) {
      timer = setTimeout(function(){
        clearTimeout(timer);
        timer = null;
        previous = +new Date();
        func.call(self, ...args)
      }, remaining)
    }
  }
}

/**
 * 手写bind
 */
Function.prototype.bind = function (context = window, ...outArgs) {
  let _this = this;
  return function(...inArgs) {
    let args = inArgs.concat(outArgs);
    _this.call(context, args);
  }
}

/**
 * 手写call
 */
Function.prototype.mycall = function (context = window, ...args) {
    let key = Symbol('key');
    context[key] = this;
    context[key](...args);
    delete context[key];
}
let obj = {
    name: 'zs'
}
function say(age) { console.log(this.name, age) }
say.mycall(obj, 18)


/**
 * compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x)，请你完成 compose函数的编写
 */
function compose(...funcs) {
  return function(...args) {
    let result, len = funcs.length;
    if(len === 0) {
      result = args
    } else if(len === 1) {
      result = funcs[0](...args)
    } else {
      result = funcs.reduce((x, y) => {
        return typeof x === 'function' ? y(x(...args)) : y(x);
      })
    }
    return result
  }
}
/**
 * 浅克隆
 */
function shadowClone(obj) {

}
/**
 * 深克隆
 */
function deepClone(obj) {

}
/**
 * let res = fn(1, 2)(3) => 6
 */
function fn(...outArgs) {
  return function anonymous(innerArgs) {
    return outArgs.concat(innerArgs).reduce(function(res, item) {
      return res + item
    }, 0)
  }
}
/**
 * let res = fn(1)(2)(3)(4)(5)
 * 基于函数运算或者输出的时候，一般都会调用函数的toString方法
 */
function curring() {
  let params = [];
  let add = function anonymous (...args) {
    params = params.concat(args)
    return add
  }
  add.toString = function() {
    return params.reduce((result, item) => result + item)
  }
  return add
}
let fn = curring();
let res = fn(1)(2)(3)(4)(5); // res => 15
/**
 * 检测某个成员（属性/键）是否属于这个对象，或者是否属于这个对象的私有属性
 * Object.getPrototypeOf获取当前实例的构造函数的原型
 * A.prototype.constructor === a.constructor
 */
function hasPubProperty(obj, attr) {
  let proto = Object.getPrototypeOf(obj);
  while(proto) {
    if(proto.hasOwnProperty(attr)) {
      return true
    }
    proto = Object.getPrototypeOf(proto)
  }
  return false
}
/**
 * 手写new源码
 */
function _new(Ctro, ...params) {
  let obj = Object.create(Ctro.prototype);
  let result = Ctro.call(obj, ...params);
  if(result !== null && /^(object|function)$/.test(typeof result)) return result;
  return obj;
}
/**
 * 数组去重
 */
Array.prototype.unique = function unique() {
  let obj = {};
      self = this;
  for(let i = 0; i < self.length; i++) {
    let item = self[i];
    if(obj.hasOwnProperty(item)) {
      self.splice(i, 1);
      i--;
      continue;
    }
    obj[item] = item;
  }
  return self
}
Array.prototype.unique = function unique() {
  return Array.from(new Set(...this))
}
/**
 * 手写call
 */
Function.prototype.call = function call(context, ...args) {
  context == null ? context = window : null;
  /^(object|function)/.test(typeof context) ? null : context = Object(context);
  let self = this,
      result = null,
      key = Symbol('Key');
  context(key) = self;
  result = context[key](...args);
  delete context[key];
  return result
}
/**
 * MyFn(‘张三’).paly(‘足球’).play(‘篮球’).sleep(3).eat(’food’)
 * 张三玩足球
 * 张三玩篮球
 * 张三吃food
 */
function MyFn(name) {
  this.name = name;
  this.things = [];
  setTimeout(() => {
    this.next()
  }, 0);
  this.next = function() {
    let fn = this.things.shift()
    fn&&fn()
    return this
  }
  this.play = function(thing) {
    let _this = this
    this.things.push(function(thing){
      console.log(_this.name + thing)
      _this.next()
    })
    return this
  }
  this.sleep = function(time) {
    let _this = this
    this.things.push(function(){
      setTimeout(()=>{
        _this.next()
      },time)
    })
    return this
  }
}
/**
 * setTimeout实现setInterval
 */
var timer = null;
function timer2Interval(flag) {
  if(flag) {
    fn = setTimeout(() => {
      console.log(1)
      timer2Interval(true)
    }, 2000);
  } else {
    clearTimeout(timer)
  }
}
/**
 * toFixed()浏览器兼容问题，数据精度存在误差
 * number：操作的数字
 * m：小数点后几位
 */
function toFixed(number, m) {
  if(typeof number !== 'number') return false
  let result = Math.round(Math.pow(10, m) * number) / Math.pow(10, m);
  result = String(result);
  if(result.indexOf(".") === '-1') {
    if(m !== 0) {
      result += ".";
      result += new Array(m+1).join("0");
    }
  } else {
    let arr = result.split('.');
    if(arr[1].length < m) {
      arr[1] += new Array(m-arr[1].length+1).join('0');
    }
    result = arr.join('.')
  }
  return result
}
/**
 * base64转blob
 */
function base64Toblob(urlData) {
  var arr = urlData.split(','), 
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
  while(n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], {type: mime})
}
/**
 * blob转base64
 */
function blobTobase64(blob) {
  let a= new FileReader();
  a.onload = function(e) {
    return e.target.result
  }
  a.readAsDataURL(blob)
}